package com.siyuan1.spring;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @className: SiyuanApplication
 * @Description: TODO
 * @author: wzq
 * @date: 2022/8/24 10:00
 */
public class SiyuanApplicationContext {

	public Class aClass;

	private ConcurrentHashMap<String,SiyuanBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
	private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

	private List<SiyuanBeanPostProcessor> beanPostProcessorList = new ArrayList<>();

	/**
	 * 1.
	 * 2.
	 * 3. 扫描路径转换成文件路径
	 * 4.
	 *
	 * @param configClass
	 */
	public SiyuanApplicationContext(Class configClass){
		try {
			this.aClass = configClass;

			scan(configClass);

			for (String beanName : beanDefinitionMap.keySet()) {
				SiyuanBeanDefinition siyuanBeanDefinition = beanDefinitionMap.get(beanName);
				if (siyuanBeanDefinition.getScope().equals("singleton") && !siyuanBeanDefinition.isLazy()) {
					Object bean = createBean(beanName, siyuanBeanDefinition);
					singletonObjects.put(beanName, bean);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	public Object createBean(String beanName,SiyuanBeanDefinition siyuanBeanDefinition) throws Exception {
		Class clazz = siyuanBeanDefinition.getClazz();

		Object instance = clazz.getConstructor().newInstance();

		// 依赖注入
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (field.isAnnotationPresent(SiyuanAutowired.class)) {
				Object bean = getBean(field.getName());
				field.setAccessible(true);
				field.set(instance,bean);
			}
		}


		// Aware
		if (instance instanceof SiyuanBeanNameAware) {
			((SiyuanBeanNameAware)instance).setBeanName(beanName);
		}

		// 初始化前 循环遍历 beanPostProcessorList 集合
		for (SiyuanBeanPostProcessor beanPostProcessor : beanPostProcessorList) {
			instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
		}

		// 初始化接口 InitializingBean
		if (instance instanceof SiyuanInitializingBean){
			((SiyuanInitializingBean)instance).afterPropertiesSet();
		}

		// 初始化后 循环遍历 beanPostProcessorList 集合
		for (SiyuanBeanPostProcessor beanPostProcessor : beanPostProcessorList) {
			instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
		}




		return instance;
	}

	public Object getBean(String beanName){
		try {
			if (!beanDefinitionMap.containsKey(beanName)){
				throw new NullPointerException();
			}
			SiyuanBeanDefinition siyuanBeanDefinition = beanDefinitionMap.get(beanName);
			// 多例
			if (!"singleton".equals(siyuanBeanDefinition.getScope())){
				return createBean(beanName,siyuanBeanDefinition);
			}
			Object bean = singletonObjects.get(beanName);
			if (ObjectUtil.isEmpty(bean)){
				bean = createBean(beanName,siyuanBeanDefinition);
				singletonObjects.put(beanName,bean);
			}
			return bean;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 包扫描
	 * @param configClass
	 * @throws ClassNotFoundException
	 */
	private void scan(Class configClass) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
		if (configClass.isAnnotationPresent(SiyuanComponentScan.class)) {
			// 获取类上对应的注解
			SiyuanComponentScan siyuanComponentScan = (SiyuanComponentScan) configClass.getAnnotation(SiyuanComponentScan.class);
			// 获取注解上指定的扫描路径 com.siyuan.spring 格式
			String siyuanComponentValue = siyuanComponentScan.value();
			String path = siyuanComponentValue.replace(".", "/");
			System.out.println(path);

			ClassLoader classLoader = this.getClass().getClassLoader();
			URL resource = classLoader.getResource(path);
			File file = new File(resource.getFile());

			if (file.isDirectory()) {
				for (File f : file.listFiles()) {
					String absolutePath = f.getAbsolutePath();
					System.out.println(absolutePath);
					absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
					String className = absolutePath.replace("/", ".");

					Class<?> clazz = classLoader.loadClass(className);
					// 有  SiyuanComponent 注解
					if (clazz.isAnnotationPresent(SiyuanComponent.class)) {

						// 实现了 SiyuanBeanPostProcessor 存放在 beanPostProcessorList 集合中
						if (SiyuanBeanPostProcessor.class.isAssignableFrom(clazz)){
							SiyuanBeanPostProcessor siyuanBeanPostProcessor = (SiyuanBeanPostProcessor) clazz.getConstructor().newInstance();
							beanPostProcessorList.add(siyuanBeanPostProcessor);
						}

						SiyuanBeanDefinition siyuanBeanDefinition = new SiyuanBeanDefinition();
						siyuanBeanDefinition.setClazz(clazz);
						siyuanBeanDefinition.setLazy(false);
						if (clazz.isAnnotationPresent(SiyuanScope.class) && StrUtil.isNotBlank(clazz.getAnnotation(SiyuanScope.class).value())) {
							siyuanBeanDefinition.setScope(clazz.getAnnotation(SiyuanScope.class).value());
						}else {
							siyuanBeanDefinition.setScope("singleton");
						}
						String beanName = clazz.getAnnotation(SiyuanComponent.class).value();
						// 为空
						if (StrUtil.isBlank(beanName)){
							beanName = Introspector.decapitalize(clazz.getSimpleName());
						}

						beanDefinitionMap.put(beanName,siyuanBeanDefinition);
					}
				}
			}
		}
	}




}
